#include "deploy/m_srt_deploy.h"
#include "m_srt.h"
#include "m_public.h"
#include "m_mutex.h"
#include "m_list.h"
#include "m_memory.h"
#include <string.h>


/**
 * 属性节点 结构体
 */
typedef struct TSrtNodeTag
{
    MSrtTable pid;

    UInt32 timestamp;   // 设置的时间戳
    UInt16 value_size;  // 值的容量
    UInt16 value_len;   // 值的长度
    UInt8  value[0];    // 值
} TSrtNode;

typedef struct TSrtObjTag
{
    MMutex lock_;  // 锁
    MList  srt_list_;  // 属性列表
} TSrtObj;

static Bool    is_init_ = False;  // 是否初始化
static TSrtObj m_srt_;

/**
 * @brief 属性初始化
 *
 * @return 执行结果 详见 TMErrCode
*/
Int32 MSrtInit(void)
{
    if (! is_init_)
    {
        MMutexCreate(&m_srt_.lock_);
        MListCreate(&m_srt_.srt_list_);

        is_init_ = True;
    }

    return kErrCodeSucc;
}

/**
 * @brief 属性去初始化
 *
 * @return 执行结果 详见 TMErrCode
*/
Int32 MSrtFini(void)
{
    if (is_init_)
    {
        is_init_ = False;

        MListDestroy(&m_srt_.srt_list_);
        MMutexDestory(&m_srt_.lock_);
    }

    return kErrCodeSucc;
}

static TSrtNode* DoFind(MSrtTable pid)
{
    TSrtNode* node = NULL;

    MListIteratorReset(&m_srt_.srt_list_);
    while (node = MListIteratorPrev(&m_srt_.srt_list_))  // 从后往前 先找热度更高的pid
    {
        if (node->pid == pid)
        {
            return node;
        }
    }

    return NULL;
}

/**
 * 获取属性是否被设置过
 *
 * @param pid 属性ID，详见 MSrtTable
 * @return Bool 是否设置过
 */
Int32 MSrtIsSet(MSrtTable pid, Bool *is_set)
{
    if (is_set == NULL)
    {
        return kErrCodeParamsErr;
    }

    if (! is_init_)
    {
        return kErrCodeNotCreate;
    }

    Int32 result = kErrCodeElse;

    if (MMutexEnter(&m_srt_.lock_))
    {
        if (DoFind(pid) != NULL)
        {
            *is_set = True;
        }
        else
        {
            *is_set = False;
        }

        MMutexLeave(&m_srt_.lock_);

        result = kErrCodeSucc;
    }
    else
    {
        result = kErrCodeGetLockErr;
    }

    return result;
}

/**
 * 获取属性时间戳
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param timestamp 输出缓冲区
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtGetTimestamp(MSrtTable pid, UInt32 *timestamp)
{
    if (timestamp == NULL)
    {
        return kErrCodeParamsErr;
    }

    if (! is_init_)
    {
        return kErrCodeNotCreate;
    }

    Int32 result = kErrCodeElse;

    if (MMutexEnter(&m_srt_.lock_))
    {
        TSrtNode* node = DoFind(pid);

        if (node != NULL)
        {
            *timestamp = node->timestamp;
            result = kErrCodeSucc;
        }
        else
        {
            result = kErrCodeItemNotExist;
        }

        MMutexLeave(&m_srt_.lock_);
    }
    else
    {
        result = kErrCodeGetLockErr;
    }

    return result;
}

static Int32 DoGetValue(MSrtTable pid, UInt8* buf, Int32 buf_size)
{
    if (buf == NULL)
    {
        return kErrCodeParamsErr;
    }

    if (! is_init_)
    {
        return kErrCodeNotCreate;
    }

    Int32 result = kErrCodeElse;

    if (MMutexEnter(&m_srt_.lock_))
    {
        TSrtNode* node = DoFind(pid);

        if (node != NULL)
        {
            if (buf_size >= node->value_len)
            {
                memcpy(buf, node->value, node->value_len);
                result = node->value_len;
            }
            else
            {
                result = kErrCodeOutOfSpace;
            }
        }
        else
        {
            result = kErrCodeItemNotExist;
        }

        MMutexLeave(&m_srt_.lock_);
    }
    else
    {
        result = kErrCodeGetLockErr;
    }

    return result;
}

static Int32 DoSetValue(MSrtTable pid, UInt8* data, Int32 data_len, Bool realloc)
{
    if (data == NULL)
    {
        return kErrCodeParamsErr;
    }

    if (! is_init_)
    {
        return kErrCodeNotCreate;
    }

    Int32 result = kErrCodeElse;

    if (MMutexEnter(&m_srt_.lock_))
    {
        TSrtNode* node = DoFind(pid);

        if (node != NULL)
        {
            if (node->value_size >= data_len)
            {
                memcpy(node->value, data, data_len);
                node->value_len = data_len;
                result = kErrCodeSucc;
            }
            else if (realloc)
            {
                TSrtNode* new_node = MMalloc(sizeof(TSrtNode) + data_len);

                if (new_node != NULL)
                {
                    MListIteratorPop(&m_srt_.srt_list_);
                    new_node->pid = node->pid;
                    new_node->timestamp = MGetTicks();
                    new_node->value_size = data_len;
                    new_node->value_len = data_len;
                    memcpy(new_node->value, data, data_len);
                    if (MListPush(&m_srt_.srt_list_, new_node) == kErrCodeSucc)
                    {
                        result = kErrCodeSucc;
                    }
                    else
                    {
                        MFree(new_node);
                    }
                    MFree(node);
                }
                else
                {
                    result = kErrCodeMallocFail;
                }
            }
            else
            {
                result = kErrCodeOutOfSpace;
            }
        }
        else
        {
            TSrtNode* new_node = MMalloc(sizeof(TSrtNode) + data_len);

            if (new_node != NULL)
            {
                new_node->pid = pid;
                new_node->timestamp = MGetTicks();
                new_node->value_size = data_len;
                new_node->value_len = data_len;
                memcpy(new_node->value, data, data_len);
                if (MListPush(&m_srt_.srt_list_, new_node) == kErrCodeSucc)
                {
                    result = kErrCodeSucc;
                }
                else
                {
                    MFree(new_node);
                }
            }
            else
            {
                result = kErrCodeMallocFail;
            }
        }

        MMutexLeave(&m_srt_.lock_);
    }
    else
    {
        result = kErrCodeGetLockErr;
    }

    return result;
}

/**
 * 获取Bool类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 输出缓冲区
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtGetBool(MSrtTable pid, Bool *value)
{
    return DoGetValue(pid, (UInt8 *)value, sizeof(Bool));
}

/**
 * 设置Bool类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 设置参数值
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtSetBool(MSrtTable pid, Bool value)
{
    return DoSetValue(pid, (UInt8 *)&value, sizeof(Bool), False);
}

/**
 * 获取UInt8类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 输出缓冲区
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtGetUInt8(MSrtTable pid, UInt8 *value)
{
    return DoGetValue(pid, (UInt8 *)value, sizeof(UInt8));
}

/**
 * 设置UInt8类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 设置参数值
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtSetUInt8(MSrtTable pid, UInt8 value)
{
    return DoSetValue(pid, (UInt8 *)&value, sizeof(UInt8), False);
}

/**
 * 获取UInt16类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 输出缓冲区
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtGetUInt16(MSrtTable pid, UInt16 *value)
{
    return DoGetValue(pid, (UInt8 *)value, sizeof(UInt16));
}

/**
 * 设置UInt16类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 设置参数值
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtSetUInt16(MSrtTable pid, UInt16 value)
{
    return DoSetValue(pid, (UInt8 *)&value, sizeof(UInt16), False);
}

/**
 * 获取UInt32类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 输出缓冲区
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtGetUInt32(MSrtTable pid, UInt32 *value)
{
    return DoGetValue(pid, (UInt8 *)value, sizeof(UInt32));
}

/**
 * 设置UInt32类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 设置参数值
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtSetUInt32(MSrtTable pid, UInt32 value)
{
    return DoSetValue(pid, (UInt8 *)&value, sizeof(UInt32), False);
}

/**
 * 获取Float类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 输出缓冲区
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtGetFloat(MSrtTable pid, Float *value)
{
    return DoGetValue(pid, (UInt8 *)value, sizeof(Float));
}

/**
 * 设置Float类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 设置参数值
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtSetFloat(MSrtTable pid, Float value)
{
    return DoSetValue(pid, (UInt8 *)&value, sizeof(Float), False);
}

/**
 * 获取Double类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 输出缓冲区
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtGetDouble(MSrtTable pid, Double *value)
{
    return DoGetValue(pid, (UInt8 *)value, sizeof(Double));
}

/**
 * 设置Double类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param value 设置参数值
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtSetDouble(MSrtTable pid, Double value)
{
    return DoSetValue(pid, (UInt8 *)&value, sizeof(Double), False);
}

/**
 * 获取Bin类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param buf 输出缓冲区
 * @param buf_size 输出缓冲区的大小
 * @retval >=0 获取长度
 * @retval <0 错误码，详见 MErrCode
 */
Int32 MSrtGetBin(MSrtTable pid, UInt8* buf, Int32 buf_size)
{
    return DoGetValue(pid, buf, buf_size);
}

/**
 * 设置Bin类型属性
 *
 * @param pid 属性ID，详见 MSrtTable
 * @param data 输入数据
 * @param data_len 输入数据的长度
 * @return 执行结果，详见 MErrCode
 */
Int32 MSrtSetBin(MSrtTable pid, UInt8* data, Int32 data_len)
{
    return DoSetValue(pid, data, data_len, True);
}
